home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue51 / Ligen / LIGen.dpr next >
Encoding:
Text File  |  1999-10-06  |  19.3 KB  |  659 lines

  1. program LIGen;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses SysUtils, Registry, LIUtils, Windows;
  6.  
  7. type
  8.   TCompileStatus = (csNone, csCompile, csMake, csBuild);
  9.  
  10. var
  11.   ExtraCmdLine:  String = '';
  12.   ProjectName:   String = '';
  13.   OutputDir:     String = '';
  14.   DCC32ExecName: String = 'DCC32.EXE';
  15.   OptFileName:   String;
  16.   MapFileName:   String;
  17.   CfgFileName:   String;
  18.   UseDCCVer:     String;
  19.   CompileStatus: TCompileStatus = csNone;
  20.   OverwriteCfg:  Boolean = False;
  21.   PauseOnError:  Boolean = False;
  22.   DCC32CfgOnly:  Boolean = False;
  23.   DontGenerateDCC32Cfg: Boolean = False;
  24.   CfgFile:       TextFile;
  25.   UnitTable:     TGrowingArray;
  26.   PublicList:    TGrowingArray;
  27.   LineNumbers:   TGrowingArray;
  28.   Resource:      TGrowingArray;
  29.   RTLIHeader:    TRTLIHeader;
  30.  
  31. // Displays command line syntax and terminates
  32.  
  33. procedure DisplaySyntax;
  34. begin
  35.   WriteLn('Syntax: LIGen [Options] ProjectFile');
  36.   WriteLn('/N           create DCC32.CFG only, do Not compile and generate RTLI');
  37.   WriteLn('/O           Overwrite DCC32.CFG');
  38.   WriteLn('/P           Pause on error: wait for the Enter key to be pressed');
  39.   WriteLn('/Rb,/Rc,/Rm  Run DCC32 only: Rb=build, Rc=compile, Rm=make project');
  40.   WriteLn('/S<x>        pass command line Switch -<x> directly to DCC32');
  41.   WriteLn('/V<X.0>      Use DCC32 for version X.0 of Delphi');
  42.   WriteLn('/?,/H        display this Help screen');
  43.   Halt(1);
  44. end;
  45.  
  46. // Displays an error message and terminates
  47.  
  48. procedure Error(const ErrStr: String; const Params: array of const);
  49. begin
  50.   WriteLn('**Error**  ', Format(ErrStr, Params));
  51.   if PauseOnError then
  52.   begin
  53.     WriteLn('Press Enter to exit');
  54.     ReadLn;
  55.   end;
  56.   Halt(2);
  57. end;
  58.  
  59. // Reports an invalid command line option error
  60.  
  61. procedure InvalidCmdLineOption(const ParmStr: String);
  62. begin
  63.   Error('Invalid command line option "%s"', [ParmStr]);
  64. end;
  65.  
  66. // Reports an unsuccessful compilation
  67.  
  68. procedure CompilationFailed(ExitCode: Integer);
  69. begin
  70.   Error('Compilation failed, return code = %d', [ExitCode]);
  71. end;
  72.  
  73. // Parses the supplied command line
  74.  
  75. procedure ParseCmdLine;
  76. var
  77.   ParmIndex: Integer;
  78.   ParmStr: String;
  79. begin
  80.   ParmIndex := 1;
  81.   ParmStr := '';
  82.   repeat
  83.     ParmStr := ParamStr(ParmIndex);
  84.     Inc(ParmIndex);
  85.     if ParamCount = 0 then ParmStr := '/?';
  86.     if (ParmStr <> '') then
  87.     begin
  88.       if not (ParmStr[1] in ['-', '/']) then
  89.         ProjectName := ExpandFileName(ParmStr)
  90.       else
  91.         case Length(ParmStr) of
  92.           2:
  93.             case UpCase(ParmStr[2]) of
  94.               'D': DontGenerateDCC32Cfg := True;
  95.               'N': DCC32CfgOnly  := True;
  96.               'O': OverwriteCfg := True;
  97.               'P': PauseOnError := True;
  98.               '?','H': DisplaySyntax;
  99.               else InvalidCmdLineOption(ParmStr);
  100.             end;
  101.           3..1024:
  102.             case UpCase(ParmStr[2]) of
  103.               'S':
  104.                 begin
  105.                   if ExtraCmdLine <> '' then
  106.                     ExtraCmdLine := ExtraCmdLine + ' ';
  107.                   ExtraCmdLine := ExtraCmdLine + '-' + Copy(ParmStr, 3, MaxInt);
  108.                 end;
  109.               'V':
  110.                 begin
  111.                   UseDCCVer := Copy(ParmStr, 3, 3);
  112.                 end;
  113.               'R':
  114.                 begin
  115.                   if (Length(ParmStr) <> 3) or not (UpCase(ParmStr[3]) in ['B','C','M']) then
  116.                     InvalidCmdLineOption(ParmStr);
  117.                   case UpCase(ParmStr[3]) of
  118.                     'B': CompileStatus := csBuild;
  119.                     'C': CompileStatus := csCompile;
  120.                     'M': CompileStatus := csMake;
  121.                   end;
  122.                 end;
  123.               else InvalidCmdLineOption(ParmStr);
  124.             end;
  125.         end;
  126.     end;
  127.   until ParmStr = '';
  128.   if ProjectName = '' then
  129.     DisplaySyntax;
  130. end;
  131.  
  132. // The following three functions are similar to the corresponding Dos unit
  133. // functions found in Borland Pascal. Unfortunately, Dos unit disappeared
  134. // in Delphi and there are no equivalent functions in the SysUtils unit.
  135. // So we have to implement them here.
  136.  
  137. var
  138.   ProcessInfo: TProcessInformation;
  139.  
  140. function Exec(const Path,CmdLine: String): Integer;
  141. var
  142.   Win32Path: String;
  143.   Win32CmdLine: String;
  144.   StartupInfo: TStartupInfo;
  145. begin
  146.   Win32Path := ExpandFileName(Path);
  147.   if Win32Path <> '' then
  148.     if Win32Path[1] <> '"' then
  149.       Win32Path := '"' + Win32Path + '"';
  150.   Win32CmdLine := Win32Path + ' ' + CmdLine;
  151.   FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
  152.   with StartupInfo do
  153.   begin
  154.     cb := SizeOf(TStartupInfo);
  155.     dwFlags := startf_UseShowWindow;
  156.     wShowWindow := sw_ShowNormal;
  157.   end;
  158.   if CreateProcess(nil, PChar(Win32CmdLine), nil, nil, True, normal_Priority_Class, nil, nil, StartupInfo, ProcessInfo) then
  159.     begin
  160.       WaitForSingleObject(ProcessInfo.hProcess, Infinite);
  161.       Result := 0;
  162.     end
  163.   else
  164.     Result := GetLastError;
  165. end;
  166.  
  167. function DosExitCode: DWord;
  168. begin
  169.   GetExitCodeProcess(ProcessInfo.hProcess, Result);
  170. end;
  171.  
  172. function GetEnv(const EnvVar: String): String;
  173. var
  174.   Buffer: array[0..1023] of Char;
  175. begin
  176.   SetString(Result, Buffer,
  177.     GetEnvironmentVariable(PChar(EnvVar), Buffer, SizeOf(Buffer)));
  178. end;
  179.  
  180. // Reads an integer value from the INI file
  181.  
  182. function OptReadInteger(const AppName,KeyName: String; Default: Integer): Integer;
  183. begin
  184.   Result := GetPrivateProfileInt(PChar(AppName), PChar(KeyName), Default,
  185.     PChar(OptFileName));
  186. end;
  187.  
  188. // Reads a string value from the INI file
  189.  
  190. function OptReadString(const AppName,KeyName,Default: String): String;
  191. var
  192.   Buffer: array[0..259] of Char;
  193. begin
  194.   GetPrivateProfileString(PChar(AppName), PChar(KeyName), PChar(Default),
  195.     Buffer, SizeOf(Buffer), PChar(OptFileName));
  196.   Result := Buffer;
  197. end;
  198.  
  199. procedure AppendToCmdLine(const S: String);
  200. var
  201.   C: Char;
  202. begin
  203.   if S = '(' then
  204.     begin
  205.       CfgFileName := ExtractFilePath(OptFileName) + '\DCC32.CFG';
  206.       C := 'Y';
  207.       if FileExists(CfgFileName) then
  208.       begin
  209.         if not OverwriteCfg then
  210.         begin
  211.           Write('File DCC32.CFG already exists. Overwrite? (Y/N)');
  212.           repeat
  213.             ReadLn(C);
  214.           until C in ['Y', 'N', 'y', 'n'];
  215.         end;
  216.       end;
  217.       if UpCase(C) = 'N' then
  218.         Halt(3);
  219.       AssignFile(CfgFile, CfgFileName);
  220.       Rewrite(CfgFile);
  221.       if IOResult <> 0 then
  222.         Error('Cannot create CFG file "%s"', [CfgFileName]);
  223.     end
  224.   else
  225.     if S = ')' then
  226.       begin
  227.         CloseFile(CfgFile);
  228.         IOResult; //  := 0;
  229.       end
  230.     else
  231.       begin
  232.         WriteLn(CfgFile, S);
  233.         if IOResult <> 0 then
  234.           Error('Error writing file "%s" - %s', [CfgFileName, SysErrorMessage(IOResult)]);
  235.       end;
  236. end;
  237.  
  238. // Forms the command line compiler configuration file based on the settings
  239. // found in the project option file
  240.  
  241. procedure FormDCC32Config;
  242. var
  243.   C,State: Char;
  244.   Value: Integer;
  245.   CfgStr: String;
  246. begin
  247.   OptFileName := ChangeFileExt(ProjectName, '.DOF');
  248.   if not FileExists(OptFileName) then
  249.     Error('Cannot find project option file "%s"', [OptFileName]);
  250.   CfgStr := '';
  251.   for C := 'A' to 'Z' do
  252.   begin
  253.     Value := OptReadInteger('Compiler', C, 2);
  254.     case Value of
  255.       0: State := '-';
  256.       1: State := '+';
  257.       else
  258.         if C = 'A' then
  259.           Error('Invalid Delphi options file "%s"', [OptFileName]);
  260.         State := '-';
  261.     end;
  262.     CfgStr := Format('%s-$%s%s ', [CfgStr, C, State]);
  263.   end;
  264.   AppendToCmdLine('(');
  265.   AppendToCmdLine(CfgStr);
  266.   case CompileStatus of
  267.     csMake:  AppendToCmdLine('-M');
  268.     csBuild: AppendToCmdLine('-B');
  269.   end;
  270.   if OptReadInteger('Compiler', 'ShowHints', 2) = 1 then
  271.     AppendToCmdLine('-H');
  272.   if OptReadInteger('Compiler', 'ShowWarnings', 2) = 1 then
  273.     AppendToCmdLine('-W');
  274.   CfgStr := OptReadString('Compiler', 'UnitAliases', '');
  275.   if CfgStr <> '' then
  276.     AppendToCmdLine('-A' + CfgStr);
  277.   OutputDir := OptReadString('Directories', 'OutputDir', '');
  278.   if OutputDir <> '' then
  279.     AppendToCmdLine('-E"' + OutputDir + '"');
  280.   CfgStr := OptReadString('Directories', 'SearchPath', '');
  281.   if CfgStr <> '' then
  282.   begin
  283.     AppendToCmdLine('-U"' + CfgStr + '"');
  284.     AppendToCmdLine('-I"' + CfgStr + '"');
  285.     AppendToCmdLine('-R"' + CfgStr + '"');
  286.     AppendToCmdLine('-O"' + CfgStr + '"');
  287.   end;
  288.   CfgStr := OptReadString('Directories', 'Conditionals', '');
  289.   if CfgStr <> '' then
  290.     AppendToCmdLine('-D' + CfgStr);
  291.   C := #0;
  292.   case OptReadInteger('Linker', 'MapFile', 0) of
  293.     1: C := 'S';
  294.     2: C := 'P';
  295.     3: C := 'D';
  296.   end;
  297.   if C <> #0 then
  298.     AppendToCmdLine('-G' + C);
  299.   if OptReadInteger('Linker', 'OutputObjs', 0) <> 0 then
  300.     AppendToCmdLine('-J');
  301.   C := 'C';
  302.   if OptReadInteger('Linker', 'ConsoleApp', 0) = 1 then
  303.     C := 'G';
  304.   AppendToCmdLine('-C' + C);
  305.   if OptReadInteger('Linker', 'DebugInfo', 0) <> 0 then
  306.     AppendToCmdLine('-V');
  307.   AppendToCmdLine(Format('-M%d,%d',
  308.     [OptReadInteger('Linker', 'MinStackSize', 16384),
  309.      OptReadInteger('Linker', 'MaxStackSize', 1048576)]));
  310.   AppendToCmdLine(Format('-K%x', [OptReadInteger('Linker', 'ImageBase', $400000)]));
  311.   AppendToCmdLine(')');
  312. end;
  313.  
  314.  
  315. // Runs the command line compiler
  316. function CompileProject(const CtrlParam: String): Integer;
  317. var
  318.   DccParms : string;
  319.   ErrCode: Integer;
  320.   DCCName: String;
  321.   Registry: TRegistry;
  322.  
  323.   function HasPathForVersion(const Version: string): boolean;
  324.   begin
  325.     Result := Registry.OpenKey('\SOFTWARE\Borland\Delphi\'+Version, False);
  326.     if Result then
  327.     begin
  328.       DCCName := Registry.ReadString('RootDir');
  329.       if DCCName <> '' then
  330.         DCCName := DCCName + '\BIN\DCC32.EXE';
  331.     end;
  332.   end;
  333.  
  334. begin
  335.   Registry := TRegistry.Create;
  336.   Registry.RootKey := hkey_Local_Machine;
  337.   try
  338.     // Has the user specificed the compiler version?
  339.     if UseDCCVer <> '' then
  340.       HasPathForVersion(UseDCCVer)
  341.     else
  342.     begin
  343.       // Try to find the command line compiler executable
  344.       DCCName := FileSearch(DCC32ExecName, GetEnv('PATH'));
  345.       if DCCName <> '' then
  346.         DCCName := ExpandFileName(DCCName)
  347.       else
  348.       begin
  349.         // Executable is not found in 'PATH'. Try to find the Delphi directory
  350.         // setting RootDir in the Registry under
  351.         // HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Delphi\X.0
  352.         if HasPathForVersion('5.0') or
  353.            HasPathForVersion('4.0') or
  354.            HasPathForVersion('3.0') or
  355.            HasPathForVersion('2.0') then
  356.           ;
  357.       end;
  358.     end;
  359.   finally
  360.     Registry.Destroy; // Hmm, looks a bit severe :-)
  361.   end;
  362.   if DCCName = '' then
  363.     Error('Cannot find file %s, make sure it is included in PATH, or use the /V option', [DCC32ExecName]);
  364.   // Change to the directory where the project is located
  365.   SetCurrentDir(ExtractFilePath(OptFileName));
  366.   // Run the command line compiler
  367.  
  368. {John Wilson+ Place ProjectName in quotes}
  369.   DccParms := Concat ('"', ProjectName, '" ', ExtraCmdLine, CtrlParam);
  370.   WriteLn (' * Dcc32 parameters: ' +DccParms);
  371.   ErrCode := Exec(DCCName, DccParms);
  372. {John Wilson+}
  373.   if ErrCode <> 0 then
  374.     Error('Cannot execute %s - %s', [DCCName, SysErrorMessage(ErrCode)]);
  375.   Result := DosExitCode;
  376. end;
  377.  
  378. // Figures out the name of the .MAP file
  379.  
  380. procedure GetMapFileName;
  381. begin
  382.   if OutputDir = '' then
  383.     MapFileName := ProjectName
  384.   else
  385.     begin
  386.       MapFileName := ExtractFileName(ProjectName);
  387.       if OutputDir[Length(OutputDir)] <> '\' then
  388.         MapFileName := OutputDir + '\' + MapFileName
  389.       else
  390.         MapFileName := OutputDir + MapFileName;
  391.     end;
  392.   MapFileName := ChangeFileExt(MapFileName, '.MAP');
  393. end;
  394.  
  395. // Parses a map file
  396.  
  397. procedure ParseMapFile;
  398. var
  399.   C: Char;
  400.   I,MapLineNo,SegNo,LnNo,LnOfs,LastLnNo,LastOfs,CurOfs,CodeEnd: Integer;
  401.   Buffer: array[0..299] of Char;
  402.   S,Name,SrcName: String;
  403.   MapFile: Text;
  404.  
  405. procedure InvalidMapFile;
  406. begin
  407.   Error('Invalid format of the map file "%s" at line %d', [MapFileName, MapLineNo]);
  408. end;
  409.  
  410. procedure ReadMapLine;
  411. begin
  412.   ReadLn(MapFile, S);
  413.   Inc(MapLineNo);
  414.   if IOResult <> 0 then
  415.     Error('Error reading map file "%s" - %s', [MapFileName, SysErrorMessage(IOResult)]);
  416. end;
  417.  
  418. procedure WriteData(const A: TGrowingArray; Size: Integer);
  419. begin
  420.   Move(Buffer, A.Allocate(Size)^, Size);
  421. end;
  422.  
  423. begin
  424.   UnitTable   := TGrowingArray.Create(512, 512, 1);
  425.   PublicList  := TGrowingArray.Create(8*1024, 8*1024, 1);
  426.   LineNumbers := TGrowingArray.Create(8*1024, 8*1024, 1);
  427.   Resource    := TGrowingArray.Create(16*1024, 16*1024, 1);
  428.   FillChar(RTLIHeader, SizeOf(RTLIHeader), 0);
  429.   Assign(MapFile, MapFileName);
  430.   Reset(MapFile);
  431.   if IOResult <> 0 then
  432.     Error('Cannot open file "%s" - %s', [MapFileName, SysErrorMessage(IOResult)]);
  433.   // Parse detailed segment map, for example:
  434.   // 0001:00000000 00000B90 C=CODE     S=.text    G=(none)   M=System   ACBP=A9
  435.   // 0001:00000B90 00000019 C=CODE     S=.text    G=(none)   M=PROGRAM  ACBP=A9
  436.   MapLineNo := 0;
  437.   while not EOF(MapFile) do
  438.   begin
  439.     ReadMapLine;
  440.     if S = 'Detailed map of segments' then
  441.       Break;
  442.   end;
  443.   if EOF(MapFile) then
  444.     InvalidMapFile;
  445.   ReadMapLine;
  446.   CodeEnd := 0;
  447.   repeat
  448.     ReadMapLine;
  449.     if S <> '' then
  450.     begin
  451.       I := 1;
  452.       SkipBlanks(S, I);
  453.       SegNo := ParseHex(S, I);
  454.       if SegNo > 1 then
  455.         Break;
  456.       C := ParseChr(S, I);
  457.       CurOfs := ParseHex(S, I);
  458.       SkipBlanks(S, I);
  459.       CodeEnd := ParseHex(S, I) + CurOfs;
  460.       I := Pos('M=', S);
  461.       Name := '';
  462.       if I > 0 then
  463.       begin
  464.         Inc(I, 2);
  465.         Name := ParseStr(S, I);
  466.       end;
  467.       if (C <> ':') or (SegNo = -1) or (CurOfs = -1) or (Name = '') then
  468.         InvalidMapFile;
  469.       PDWord(@Buffer)^ := CurOfs;
  470.       WriteData(UnitTable, SizeOf(DWord));
  471.       WriteData(UnitTable, EncodeString(Name, Buffer));
  472.       Inc(RTLIHeader.rtliUnitCount);
  473.     end;
  474.   until S = '';
  475.   // Ending code offset
  476.   PDWord(@Buffer)^ := CodeEnd;
  477.   WriteData(UnitTable, SizeOf(DWord));
  478.   // Parse public table, for example
  479.   // 0001:00000000       TextStart
  480.   // 0001:00000234       @HandleFinally
  481.   // 0001:0000026C       @SafeCall
  482.   while not EOF(MapFile) do
  483.   begin
  484.     ReadMapLine;
  485.     if Pos('Publics by Value', S) <> 0 then
  486.     begin
  487.       ReadMapLine;
  488.       Break;
  489.     end;
  490.   end;
  491.   LastOfs := 0;
  492.   if EOF(MapFile) then
  493.     InvalidMapFile;
  494.   repeat
  495.     ReadMapLine;
  496.     I := 1;
  497.     SkipBlanks(S, I);
  498.     SegNo := ParseHex(S, I);
  499.     if SegNo = 1 then
  500.     begin
  501.       C := ParseChr(S, I);
  502.       CurOfs := ParseHex(S, I);
  503.       SkipBlanks(S, I);
  504.       Name := ParseStr(S, I);
  505.       if (C <> ':') or (CurOfs = -1) or (Name = '') then
  506.         InvalidMapFile;
  507.       WriteData(PublicList, EncodeString(Name, Buffer));
  508.       WriteData(PublicList, EncodeSymbolOfs(Buffer, CurOfs - LastOfs));
  509. {      if Name = '___Fixup___' then
  510.         RTLIHeader.rtliFixup := CurOfs;}
  511.       Inc(RTLIHeader.rtliPublicCount);
  512.       LastOfs := CurOfs;
  513.     end;
  514.   until S = '';
  515.   // Terminating entry
  516.   Buffer[0] := #0;
  517.   WriteData(PublicList, 1);
  518.   WriteData(PublicList, EncodeSymbolOfs(Buffer, CodeEnd - LastOfs));
  519.   // Parse line number information, for example
  520.   // Line numbers for MyProg(myprog.pas) segment .text
  521.   //
  522.   //   1 0001:00000B90     2 0001:00000BA0
  523.   while not EOF(MapFile) do
  524.   begin
  525.     ReadMapLine;
  526.     I := Pos('Line numbers for', S);
  527.     if I <> 0 then
  528.     begin
  529.       Inc(I, 16);
  530.       SkipBlanks(S, I);
  531.       Name := ParseStr(S, I);
  532.       C := ParseChr(S, I);
  533.       SrcName := ParseStr(S, I);
  534.       if (Name = '') or (SrcName = '') or (C <> '(') then
  535.         InvalidMapFile;
  536.       Name := SrcName;
  537.       Buffer[0] := Chr(escFileName);
  538.       WriteData(LineNumbers, EncodeString(Name, @Buffer[1]) + 1);
  539.       ReadMapLine; // Skip blank line
  540.       ReadMapLine;
  541.       LastOfs := 0;
  542.       LastLnNo := 0;
  543.       repeat
  544.         I := 1;
  545.         repeat
  546.           SkipBlanks(S, I);
  547.           LnNo := ParseDec(S, I);
  548.           SkipBlanks(S, I);
  549.           SegNo := ParseHex(S, I);
  550.           C     := ParseChr(S, I);
  551.           LnOfs := ParseHex(S, I);
  552.           if (SegNo <> 1) or (C <> ':') or (LnOfs = -1) then
  553.             InvalidMapFile;
  554.           WriteData(LineNumbers, EncodeLineNumber(Buffer, LnNo - LastLnNo, LnOfs - LastOfs));
  555.           LastLnNo := LnNo;
  556.           LastOfs := LnOfs;
  557.           Inc(RTLIHeader.rtliLineCount);
  558.           SkipBlanks(S, I);
  559.         until I > Length(S);
  560.         ReadMapLine;
  561.       until S = '';
  562.     end;
  563.   end;
  564.   CloseFile(MapFile);
  565.   IOResult; // := 0;
  566. //  if RTLIHeader.rtliFixup = 0 then
  567. //    Error('RTLI is not used in the project %s', [ProjectName]);
  568. end;
  569.  
  570. procedure FormResourceFile;
  571. const
  572.   Signature: array[0..31] of Byte =
  573.     ($00,$00,$00,$00,$20,$00,$00,$00,$FF,$FF,$00,$00,$FF,$FF,$00,$00,
  574.      $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00);
  575.    ResHdr: array [0..31] of Byte =
  576.      ($00,$00,$00,$00,$20,$00,$00,$00,$FF,$FF,$0A,$00,$FF,$FF,$77,$77,
  577.       $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00);
  578.    PadBytes: array[0..2] of Byte = (0, 0, 0);
  579. var
  580.   ResSizeOfs: Integer;
  581.  
  582. procedure WriteToResource(const Buffer; Size: Integer);
  583. begin
  584.   Move(Buffer, Resource.Allocate(Size)^, Size);
  585. end;
  586.  
  587. begin
  588.   // Record identifing the resource file as a file containing 32-bit resources
  589.   WriteToResource(Signature, SizeOf(Signature));
  590.   // Resource header
  591.   ResSizeOfs := Resource.Count;
  592.   WriteToResource(ResHdr, SizeOf(ResHdr));
  593.   // Resource itself:
  594.   // RTLI Header
  595.   WriteToResource(RTLIHeader, SizeOf(RTLIHeader));
  596.   //  - Unit table
  597.   WriteToResource(UnitTable.ArrPtr^, UnitTable.Count);
  598.   UnitTable.Destroy;
  599.   //  - Public Table
  600.   WriteToResource(PublicList.ArrPtr^, PublicList.Count);
  601.   PublicList.Destroy;
  602.   //  - Line number information
  603.   WriteToResource(LineNumbers.ArrPtr^, LineNumbers.Count);
  604.   LineNumbers.Destroy;
  605.   // Align resource at DWord boundary
  606.   if (Resource.Count and $3) <> 0 then
  607.     WriteToResource(PadBytes, 4 - (Resource.Count and $3));
  608.   PDWord(PChar(Resource.ArrPtr) + ResSizeOfs)^ := Resource.Count - ResSizeOfs - SizeOf(ResHdr);
  609. end;
  610.  
  611. procedure StoreResourceFile;
  612. var
  613.   ResFileName: AnsiString;
  614.   ResFile: file;
  615. begin
  616.   ResFileName := ChangeFileExt(ProjectName, '.RLI');
  617.   Assign(ResFile, ResFileName);
  618.   Rewrite(ResFile, 1);
  619.   if IOResult <> 0 then
  620.     Error('Cannot create file "%s" - %s', [ResFileName, SysErrorMessage(IOResult)]);
  621.   BlockWrite(ResFile, Resource.ArrPtr^, Resource.Count);
  622.   if IOResult <> 0 then
  623.     Error('Error writing file "%s" - %s', [ResFileName, SysErrorMessage(IOResult)]);
  624.   Close(ResFile);
  625.   IOResult; // := 0;
  626.   Resource.Destroy;
  627. end;
  628.  
  629. var
  630.   ExitCode: Integer;
  631.  
  632. begin
  633.   WriteLn('RTLI Generator/DCC32 launcher for Delphi2  Version 1.0');
  634.   ParseCmdLine;
  635.   if not DontGenerateDCC32Cfg then
  636.     FormDCC32Config;
  637.   if not DCC32CfgOnly then
  638.   begin
  639.     if CompileStatus <> csNone then
  640.       // Terminate itself passing the exit code from the compiler.
  641.       // This ensures that MAKE process fails if the compilation is unsuccessful
  642.       Halt(CompileProject(''))
  643.     else
  644.       begin
  645.         CompileStatus := csMake;
  646.         ExitCode := CompileProject(' -GD -M');
  647.         if ExitCode <> 0 then
  648.           CompilationFailed(ExitCode);
  649.         GetMapFileName;
  650.         ParseMapFile;
  651.         FormResourceFile;
  652.         StoreResourceFile;
  653.         ExitCode := CompileProject(' -DBindingRTLI');
  654.         if ExitCode <> 0 then
  655.           CompilationFailed(ExitCode);
  656.       end;
  657.   end;
  658. end.
  659.